home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- This program is a demonstration of using arrays of structures
- for such tasks as screen painting, field definition, and data file
- searching.
-
- This program is submitted as public domain. It's basic concept is
- to communicate some uses of data structures and re-useable coding.
- This program (along with other BTree and similiar structure based
- programs) is the result of a C programming part II class I taught
- at a local university. Several of my students have stated how much
- easier project developement was with routines similiar to these.
-
- Mario Giannini
- Compuserve #76276,1576
- January 25, 1991
- *************************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <conio.h>
- #include <io.h>
- #include <fcntl.h>
- #include <sys\types.h>
- #include <sys\stat.h>
- #include <dos.h>
- #include <malloc.h> /* alloc.h for TurboC */
- #include <string.h>
- /*************************************************************************/
- /* Key macros to make life easier */
- #define ESC 0x1B
- #define F10 0x4400
- #define CURUP 0x4800
- #define CURDN 0x5000
- #define BACKSPACE 0x0008
- #define ENTER 0x000D
-
- #define TextColor 7
- #define DataColor 0xF
-
- #define INDEXFLD 0x0001
- /* Here are our basic data structure definitions */
- typedef struct {
- int H, V, Len, Option, Type, FieldNum;
- char *Text, *Data;
- } FIELDS;
-
- typedef struct {
- int Handle, RecSize;
- FIELDS *EditFields;
- char *Filename, *RecSpace;
- long Recpos;
- } RECORD;
-
- /*************************************************************************/
- /* Here are data declarations, it should be noted that the record types
- ** can be changed here, and upon re-compile all of the code should
- ** still work as always, interpreting the structures themselves
- **/
-
- FIELDS PersonFields[]={
- 0, 0, 10, 0, 0, 0, "First", NULL,
- 25, 0, 10, 0, INDEXFLD, 0, "Last", NULL,
- 0, 2, 35, 0, 0, 0, "Company", NULL,
- 0, 3, 35, 0, 0, 0, "Comment", NULL,
- 0, 0, 0, 0, 0, 0, NULL, NULL}; /* NULL for Text is terminator */
-
- RECORD Person={0, 0, PersonFields, "PERSON.DAT", NULL, 0L};
-
- FIELDS TransFields[]={
- 0, 0, 8, 0, 0, 0, "Date", NULL,
- 25, 0, 10, 0, 0, 0, "Amount", NULL,
- 0, 0, 0, 0, 0, 0, NULL, NULL}; /* NULL for Text is terminator */
- RECORD Transaction={0, 0, TransFields, "TRANS.DAT", NULL,0L};
-
- /*************************************************************************/
- /* Sort of a 'main menu' of tables we can edit */
- RECORD *AllRecords[]={&Person, &Transaction, NULL};
- /*************************************************************************/
- /**** Function prototypes ***/
- void main(void);
- void AddItem(RECORD *Record);
- void EditItem(RECORD *Record);
- int EditRecord(RECORD *Record);
- void ShowScreen(RECORD *Record);
- void ShowData(RECORD *Record);
- void Cls(int Color);
- void Locate(int H,int V);
- void PrintStr(char *Str,int Color);
- void Message(char *Str,int Wait);
- int MyGets(char *Dest,int Len);
- int GetKey(void);
- int AppendRecord(RECORD *Record);
- int LocateRecord(RECORD *Rec);
- int ReadRecord(RECORD *Record);
- int WriteRecord(RECORD *Record);
- void BlankRecord(RECORD *Record);
- int OpenFile(RECORD *Record);
- void CloseFile(RECORD *Record);
- /*************************************************************************/
- /*** Start of code section ***/
- /*** ***/
- /*************************************************************************/
- void main(void)
- {
- int i, NumOfRecs, Ch=0, Ch2;
-
- while(Ch!=ESC)
- {
- Cls(7);
- for(NumOfRecs=0;AllRecords[NumOfRecs];NumOfRecs++)
- printf("%d: %s\n", NumOfRecs+1, AllRecords[NumOfRecs]->Filename);
- printf("\nESC to quit.\n\nPlease press a key..");
- do {
- Ch=GetKey();
- } while(Ch!=ESC && (Ch<'0'||Ch-'1'>NumOfRecs));
- if(Ch==ESC)
- continue;
- Cls(7);
- printf("You Selected %s\n\n", AllRecords[Ch-'1']->Filename);
- printf("A) Add\nE) Edit\n\nESC for previous menu\n\nMake selection:");
- do {
- Ch2=getch();
- Ch2=toupper(Ch2);
- } while( Ch2!=ESC && Ch2!='A' && Ch2!='E') ;
-
- /*
- ** Note how the AllRecord array is used to process table based on user
- ** selection.
- */
- if(Ch2=='A')
- AddItem(AllRecords[Ch-'1']);
- if(Ch2=='E')
- EditItem(AllRecords[Ch-'1']);
- }
-
- }
- /*************************************************************************/
- /*** Higher-level functions ***/
- /*** ***/
- /*************************************************************************/
- void AddItem(RECORD *Record)
- {
- Cls(7);
- OpenFile(Record);
- ShowScreen(Record);
- BlankRecord(Record);
- Message("Press ESC to Abort, F10 to Add",0);
- if(EditRecord(Record)==F10)
- AppendRecord(Record);
- CloseFile(Record);
- Message("", 0);
- }
- /*************************************************************************/
- void EditItem(RECORD *Record)
- {
- Cls(7);
- OpenFile(Record);
- ShowScreen(Record);
- BlankRecord(Record);
- Message("Press ESC to Abort, F10 to start search",0);
- if(EditRecord(Record)==F10)
- {
- if(LocateRecord(Record))
- {
- Message("Press ESC to abort, F10 to save updates", 0);
- ReadRecord(Record);
- ShowData(Record);
- if(EditRecord(Record)==F10)
- WriteRecord(Record);
- }
- else
- Message("Criteria not found. Press any key to continue",1);
- }
- CloseFile(Record);
- Message("",0);
- }
- /*************************************************************************/
- /*** ScreenI/O section follows ***/
- /*** ***/
- /*************************************************************************/
- int EditRecord(RECORD *Record) /* Edits a Record */
- {
- int i=0, Ch=0, LastEntry=0;
-
- /*
- ** Note how this founction interprets whatever structures are passed
- ** to it at run-time. Not much is hard-coded.
- */
- for(LastEntry=0;Record->EditFields[LastEntry].Text;LastEntry++)
- ;
- while(Ch!=ESC && Ch!=F10)
- {
- Locate(Record->EditFields[i].H+strlen(Record->EditFields[i].Text)+1,
- Record->EditFields[i].V);
- Ch=MyGets(Record->EditFields[i].Data, Record->EditFields[i].Len);
- if( (Ch==ENTER||Ch==CURDN) && ++i>=LastEntry)
- i=0;
- if(Ch==CURUP && --i<0)
- i=LastEntry-1;
- }
- return(Ch);
- }
- /****************************************************************/
- void ShowScreen(RECORD *Record) /* Show Screen layout Data */
- {
- int i=0;
-
- for(i=0;Record->EditFields[i].Text;i++)
- {
- Locate(Record->EditFields[i].H, Record->EditFields[i].V);
- PrintStr(Record->EditFields[i].Text, TextColor);
- }
- }
- /****************************************************************/
- void ShowData(RECORD *Record) /* Show Record Data */
- {
- int i=0;
-
- for(i=0;Record->EditFields[i].Text;i++)
- {
- Locate(Record->EditFields[i].H+strlen(Record->EditFields[i].Text)+1,
- Record->EditFields[i].V);
- PrintStr(Record->EditFields[i].Data, DataColor);
- }
- }
- /****************************************************************/
- void Cls(int Color) /* Clears the screen to any color */
- {
- union REGS Regin;
-
- Regin.x.ax=0x0600;
- Regin.h.bh=(char)Color;
- Regin.x.cx=0;
- Regin.x.dx=0x1950;
- int86(0x10, &Regin, &Regin);
- Locate(0,0);
- }
- /****************************************************************/
- void Locate(int H, int V) /* Locate cursor at an Horiz, Vert coordinate */
- {
- union REGS Regin;
- Regin.h.ah=2;
- Regin.h.bh=0;
- Regin.h.dh=(char)V;
- Regin.h.dl=(char)H;
- int86(0x10, &Regin, &Regin);
- }
- /****************************************************************/
- void PrintStr(char *Str, int Color) /* Generic color print string routine */
- {
- union REGS Regin;
-
- Regin.h.ah=9;
- Regin.h.al=*Str;
- Regin.x.cx=strlen(Str);
- Regin.h.bl=(char)Color;
- Regin.h.bh=0;
- int86(0x10, &Regin, &Regin);
- while(*Str)
- {
- Regin.h.ah=0xE;
- Regin.h.al=*Str++;
- int86(0x10, &Regin, &Regin);
- }
- }
- /****************************************************************/
- void Message(char *Str, int Wait) /* Simple message display routine */
- {
- Locate(0,24);
- printf(" ");
- Locate(0,24);
- printf("%s", Str);
- if(Wait)
- GetKey();
- }
- /****************************************************************/
- int MyGets(char *Dest, int Len) /* Simple line input routine */
- {
- int i, Ch=0;
- char ThisChar[2];
-
- ThisChar[1]=*(Dest+Len)='\0'; /* Insure NULL Terminate */
- PrintStr(Dest, DataColor);
- i=strlen(Dest);
- while( (Ch=GetKey())!=ESC && Ch!=ENTER && Ch!=CURUP && Ch!=CURDN&&Ch!=F10)
- {
- if(Ch==BACKSPACE && i)
- {
- PrintStr("\x08 \x08",DataColor);
- i--;
- continue;
- }
- if(i<Len)
- {
- ThisChar[0]= *(Dest+i)=(char)Ch;
- PrintStr(ThisChar, DataColor);
- i++;
- }
- }
- return(Ch);
- }
- /****************************************************************/
- int GetKey() /* like getch, but handles function keys OK */
- {
- int i;
-
- if( (i=getch()) )
- return(i);
- return(getch()*0x100);
- }
- /*************************************************************************/
- /*** File I/O section follows ***/
- /*** ***/
- /*** Note that this section is where the DataBase Engine interface ***/
- /*** would appear to API's like ORACLE, dbVista, or Paradox. ***/
- /*** It may be useful to add new members to the FIELDS structure so ***/
- /*** that the field number is also present (as used by some APIs). ***/
- /*** ***/
- /*************************************************************************/
- int AppendRecord(RECORD *Record) /* Add record to End of file */
- {
- Record->Recpos=lseek(Record->Handle, 0L, SEEK_END);
- return(WriteRecord(Record));
- }
- /*************************************************************************/
- int LocateRecord(RECORD *Rec) /* Locate record */
- {
- char *LocalRec, *Easier;
- static char Mess[64];
- int i, Found=0, j;
-
- if( (LocalRec=calloc(Rec->RecSize, sizeof(char)))==NULL)
- return(-1);
-
- /*
- ** Just a note, since this is an all-field search we would
- ** first locate the index or key fields by which to search. But
- ** since this program has no index mechanism, this is just a comment.
- */
- for(i=0;Rec->EditFields[i].Text;i++) /* Locate index field */
- if(Rec->EditFields[i].Type&INDEXFLD)
- break;
- if(Rec->EditFields[i].Text)
- {
- Locate(0,17);
- printf("%s is an index (or key) field, I would search there\n",
- Rec->EditFields[i].Text);
- printf("first, if I had a DataBase Engine.\n");
- GetKey();
- Locate(0,17);
- printf(" \n");
- printf(" ");
-
- }
- Rec->Recpos=lseek(Rec->Handle, 0L, SEEK_SET);
- /*
- ** Now the search begins
- */
- while(!Found && read(Rec->Handle, LocalRec, Rec->RecSize)==Rec->RecSize)
- {
- Found=1;
- for(i=0;Rec->EditFields[i].Text&&Found;i++)
- {
- if( ! *Rec->EditFields[i].Data ) /* Don't test blank search fields */
- continue;
- Easier=LocalRec+(Rec->EditFields[i].Data-Rec->RecSpace);
- j=strlen(Rec->EditFields[i].Data);
- if(strnicmp(Rec->EditFields[i].Data, Easier, j))
- Found=0;
- }
- if(!Found)
- Rec->Recpos=lseek(Rec->Handle, 0L, SEEK_CUR);
- }
- free(LocalRec);
- return(Found);
- }
- /*************************************************************************/
- int ReadRecord(RECORD *Record) /* Read current record */
- {
- lseek(Record->Handle, Record->Recpos, SEEK_SET);
- return(read(Record->Handle, Record->RecSpace, Record->RecSize));
- }
- /*************************************************************************/
- int WriteRecord(RECORD *Record) /* Write current record */
- {
- lseek(Record->Handle, Record->Recpos, SEEK_SET);
- return(write(Record->Handle, Record->RecSpace, Record->RecSize));
- }
- /*************************************************************************/
- void BlankRecord(RECORD *Record) /* Empty out all fields */
- {
- int i;
- for(i=0;Record->EditFields[i].Text;i++)
- if(Record->EditFields[i].Data)
- *(Record->EditFields[i].Data)='\0';
- }
- /*************************************************************************/
- int OpenFile(RECORD *Record) /* Open file and create Edit Space for buffer */
- {
- int i, Memory=0;
-
- if( (Record->Handle=open(Record->Filename, O_RDWR|O_BINARY|O_CREAT,S_IWRITE))==-1)
- return(-2);
-
- /* Determine and allocate needed record block size */
- for(i=0;Record->EditFields[i].Text;i++)
- Memory=Memory+Record->EditFields[i].Len+1;
- if( (Record->RecSpace=calloc(Memory,sizeof(char)))==NULL)
- return(-1);
-
- /* Assign field pointers into out Data Record block */
- for(Memory=0, i=0;Record->EditFields[i].Text;i++)
- {
- Record->EditFields[i].Data=(Record->RecSpace+Memory);
- Memory=Memory+Record->EditFields[i].Len+1;
- }
- Record->RecSize=Memory;
- return(0);
- }
- /*************************************************************************/
- void CloseFile(RECORD *Record) /* Close File, free buffer */
- {
- int i;
- free(Record->RecSpace);
- close(Record->Handle);
- }
-
-
- /** End of File **/